package device

import (
	"context"
	"encoding/json"
	"math"
	"sync"
	"time"

	"gitee.com/yanwc/ebike-xiaoan-device/device/config"
	downpacket "gitee.com/yanwc/ebike-xiaoan-device/device/packet/down"
	"gitee.com/yanwc/ebike-xiaoan-device/internal/field"
	"github.com/panjf2000/gnet"
)

type (
	ErrorDevice string
)

const (
	ErrorDeviceBusy ErrorDevice = "设备忙"
)

func (e ErrorDevice) Error() string {
	return string(e)
}

type Device struct {
	Conn              gnet.Conn             // 设备socket链接
	LastUpdatedAt     time.Time             // 心跳更新时间
	Version           field.FirmwareVersion // 对应固件版本号
	Imei              field.IMEI            // 设备Imei
	Imsi              field.IMSI            // 设备Imsi
	DeviceType        field.DeviceType      // 设备类型
	OnEventNotify     config.EventNotify
	OnStatusNotify    config.StatusNotify
	OnHeartBeatNotify config.HeartBeatNotify
	ExecutingCommands *CommandCache
}

func NewDevice(c gnet.Conn,
	v field.FirmwareVersion,
	imei field.IMEI,
	imsi field.IMSI,
	dt field.DeviceType,
	eventNotify config.EventNotify,
	statusNotify config.StatusNotify,
	heartBeatNotify config.HeartBeatNotify,
) *Device {
	d := &Device{
		Conn:              c,
		Imei:              imei,
		Imsi:              imsi,
		DeviceType:        dt,
		Version:           v,
		OnEventNotify:     eventNotify,
		OnStatusNotify:    statusNotify,
		OnHeartBeatNotify: heartBeatNotify,
		ExecutingCommands: NewCommandCache(),
	}

	return d
}

func SendCmd[T any](ctx context.Context, d *Device, req downpacket.Req[T], result chan interface{}) (err error) {
	var respData chan []byte
	var msgNo field.SerialNumber
	if result != nil {
		respData = make(chan []byte)
	}

	msgNo, err = d.ExecutingCommands.GenerateMsgNo(0, respData)
	if err != nil {
		return err
	}

	out, err := downpacket.WrapDownPacket(req)
	if err != nil {
		return err
	}

	err = d.Conn.AsyncWrite(out)
	if err != nil {
		return err
	}

	if respData == nil {
		return
	}

	select {
	case data := <-respData:
		var rev interface{}
		json.Unmarshal(data, &rev)
		result <- rev
	case <-ctx.Done():
	}

	d.ExecutingCommands.DeleteMsgNo(msgNo)

	return nil
}

func (d *Device) AcceptCmdResult(msgNo field.SerialNumber, data []byte) {
	if v, ok := d.ExecutingCommands.commands[msgNo]; ok {
		v.RespData <- data
	}
}

func (d *Device) EventNotify(notifyType field.AlarmType) {
	if d.OnEventNotify != nil {
		(d.OnEventNotify)(config.Context{
			Imei:            d.Imei,
			Imsi:            d.Imsi,
			FirmwareVersion: d.Version,
		}, notifyType)
	}
}

type Command struct {
	sn       field.SerialNumber
	Id       int64
	IsUse    bool
	RespData chan []byte
}
type CommandCache struct {
	mutex    sync.Mutex
	commands map[field.SerialNumber]*Command
}

func NewCommandCache() *CommandCache {
	tmp := make(map[field.SerialNumber]*Command, math.MaxUint8)
	for i := field.SerialNumber(0); i < math.MaxUint8; i++ {
		tmp[i] = &Command{
			sn:    i,
			IsUse: false,
		}
	}

	return &CommandCache{
		commands: tmp,
	}
}

func (m *CommandCache) GenerateMsgNo(id int64, respData chan []byte) (field.SerialNumber, error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	for _, v := range m.commands {
		if !v.IsUse {
			v.Id = id
			v.IsUse = true
			v.RespData = respData

			return field.SerialNumber(v.sn), nil
		}
	}

	return 0, ErrorDeviceBusy
}

func (m *CommandCache) DeleteMsgNo(msgNo field.SerialNumber) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	if v, ok := m.commands[msgNo]; ok {
		v.Id = 0
		v.IsUse = false
		v.RespData = nil
	}
}
